package com.gowiny.ddns.aliyun.handler;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.alidns20150109.AsyncClient;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordsRequest;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordsResponse;
import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordsResponseBody;
import com.aliyun.sdk.service.alidns20150109.models.UpdateDomainRecordRequest;
import com.aliyun.sdk.service.alidns20150109.models.UpdateDomainRecordResponse;
import com.gowiny.ddns.core.handler.AbstractDdnsHandler;
import com.gowiny.ddns.core.key.AppKey;
import com.gowiny.ddns.core.utils.JsonUtils;
import com.gowiny.ddns.core.vo.DomainRecord;
import com.gowiny.ddns.core.vo.Manifest;

import darabonba.core.client.ClientOverrideConfiguration;

@Component
public class AliyunDdnsHandler extends AbstractDdnsHandler{

	private static final String NAME_ACCESS_KEY_ID = "accessKeyId";
	private static final String NAME_ACCESS_KEY_SECRET = "accessKeySecret";
	private static final String NAME_REGION = "region";
	private static final String NAME_ENDPOINT = "endpoint";
	private static Map<String,String> typeMapping;
	private static final Logger logger = LoggerFactory.getLogger(AliyunDdnsHandler.class);
	private Manifest manifest;
	private Map<String ,AsyncClient> clinetMap = new HashMap<String, AsyncClient>();
	
	
	public static Map<String, String> getTypeMapping() {
		if(null == typeMapping) {
			typeMapping = new HashMap<String, String>();
			typeMapping.put(AppKey.TYPE_IPV4, "A");
			typeMapping.put(AppKey.TYPE_IPV6, "AAAA");
		}
		return typeMapping;
	}
	@Override
	public boolean support(String provider) {
		return AppKey.DEFAULT_PROVIDER_TYPE.endsWith(provider);
	}
	private AsyncClient craeteClient(Map<String,Object> provider){
        String accessKeyId = MapUtils.getString(provider, NAME_ACCESS_KEY_ID);
        String accessKeySecret = MapUtils.getString(provider,NAME_ACCESS_KEY_SECRET);
        StaticCredentialProvider credentialProvider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(accessKeyId)
                .accessKeySecret(accessKeySecret)
                .build());

        String region = MapUtils.getString(provider, NAME_REGION);
        region = StringUtils.isEmpty(region)?"cn-beijing":region;
        String endpoint = MapUtils.getString(provider,NAME_ENDPOINT);
        endpoint = StringUtils.isEmpty(endpoint)?"alidns.cn-beijing.aliyuncs.com":endpoint;
        AsyncClient client = AsyncClient.builder()
                .region(region)
                .credentialsProvider(credentialProvider)
                .overrideConfiguration(
                        ClientOverrideConfiguration.create()
                                .setEndpointOverride(endpoint)
                )
                .build();
        return client;
    }
	protected void init(Manifest manifest) {
		if(this.manifest != manifest) {
			this.manifest = manifest;
		}
	}
	private DescribeDomainRecordsResponseBody.Record describeDomainRecord(AsyncClient client,String domainName,String rr,String type,String line) throws ExecutionException, InterruptedException {
        DescribeDomainRecordsRequest describeDomainRecordsRequest = DescribeDomainRecordsRequest.builder()
                .domainName(domainName)
                .searchMode("ADVANCED")
                .RRKeyWord(rr)
                .type(type)
                .line(line)
                .status("Enable")
                .build();
        CompletableFuture<DescribeDomainRecordsResponse> response = client.describeDomainRecords(describeDomainRecordsRequest);
        DescribeDomainRecordsResponse resp = response.get();
        Long totalCount = resp.getBody().getTotalCount();
        DescribeDomainRecordsResponseBody.Record result = null;
        if(totalCount > 0){
            DescribeDomainRecordsResponseBody.DomainRecords records = resp.getBody().getDomainRecords();
            result = records.getRecord().get(0);
        }
        return result;
    }

	
	private String getClientKey(Map<String,Object> provider) {
		StringBuilder builder = new StringBuilder();
		builder.append(MapUtils.getString(provider, AppKey.NAME_PROVIDER_TYPE))
		.append(":")
		.append(MapUtils.getString(provider, NAME_ACCESS_KEY_ID))
		.append(":")
		.append(MapUtils.getString(provider, NAME_ACCESS_KEY_SECRET))
		.append(":")
		.append(MapUtils.getString(provider, NAME_REGION))
		.append(":")
		.append(MapUtils.getString(provider, NAME_ENDPOINT));
		return builder.toString();
	}
	@Override
	protected void handleItem(Manifest manifest,DomainRecord domainRecord, String hostName, String line, String ipType, String ipValue) {
		String type = getTypeMapping().get(ipType);
		if(StringUtils.isEmpty(type)) {
			return;
		}
		Map<String,Object> provider = domainRecord.getProvider();
    	if(null == provider) {
    		provider = manifest.getProvider();
    	}
    	String clientKey = getClientKey(provider);
		AsyncClient client;
		if(!clinetMap.containsKey(clientKey)) {
			client = craeteClient(provider);
			clinetMap.put(clientKey, client);
		}else {
			client = clinetMap.get(clientKey);
		}
		try{
            DescribeDomainRecordsResponseBody.Record serverRecord = describeDomainRecord(client, domainRecord.getDomainName(),hostName,type,line);
            if(null == serverRecord){
                return;
            }
            Long ttl = domainRecord.getTtl();
            if(null == ttl){
                ttl = 600L;
            }

            if(serverRecord.getValue().equals(ipValue)){
                return;
            }

            UpdateDomainRecordRequest updateDomainRecordRequest = UpdateDomainRecordRequest.builder()
                    .recordId(serverRecord.getRecordId())
                    .rr(hostName)
                    .type(type)
                    .line(line)
                    .TTL(ttl)
                    .value(ipValue)
                    .build();
            CompletableFuture<UpdateDomainRecordResponse> response = client.updateDomainRecord(updateDomainRecordRequest);
            UpdateDomainRecordResponse resp = response.get();
            if(logger.isInfoEnabled()){
            	String recordId = resp.getBody().getRecordId();
                logger.info("update success,hostname:{},type:{},line:{},value:{},recordId:{}" ,hostName,type,line,ipValue,recordId);
            }
        }catch (Exception e){
            if(logger.isErrorEnabled()){
                logger.info("update fail,hostname:{},type:{},line:{},value:{},errMsg：{}" ,hostName,type,line,ipValue,e.getMessage());
            }
        }
	}

}
